from decimal import Decimal

from django.core.mail import EmailMultiAlternatives
from django.db import transaction
from django.db.models import F, Q
from django.forms import model_to_dict
from django.shortcuts import get_object_or_404
from django.template.loader import get_template
from django.utils import timezone
from django_tenants.utils import schema_context
from apps.location.models import Location
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTAuthentication
from datetime import datetime, timedelta

from apps.billing.models import ClientInvoice, InvoiceServices
from apps.booking.filters import date_range_filter_queryset, \
    appointment_filter_queryset, DateRangeFilter, booking_date_range_filter, appointment_list_filter_queryset
from apps.booking.helpers import create_booking, create_appointment, filtering_admin_notes, appointment_services
from apps.booking.helpers.cancel_appointment import cancel_appointment
from apps.booking.helpers.cancel_booking import cancel_booking
from apps.booking.helpers.create_appointment import save_client_services
from apps.booking.models import Booking, Appointment, AppointmentClientServices, Attendance
from apps.booking.repeating_threads import repeat_bookings, repeat_appointments
from apps.booking.signals import kiosk_check_in
from apps.booking.types import BookingStatusChoices, AppointmentStatusChoices
from apps.booking.validations import validate_booking, validate_appointment, validate_appointment_update
from apps.client.models import Client
from apps.core.pagination import paginated_queryset_response
from apps.document.models import AppointmentNotes
from apps.location.models import Room, AdminBookingDetails
from apps.users.models import Provider
from apps.users.permissions import IsProvider, IsClient
from apps.core.utils import date_time as datetime_helper
from .validations import validate_update_booking_api, handle_appointment_on_booking_update
from rest_framework import status

from .validations.update_appointment import update_appointment
from ..location.filters import booking_filter_queryset
from ..tenant.models import ProviderLocation
from django.conf import settings


def send_checked_in_template(user, client, appointment):
    from_email = settings.CONTACT_EMAIL
    to = user.email
    subject = 'Ethera Check In!'
    html_template = get_template('booking/check_in_template.html')
    context = {
        'full_name_client': client.first_name + ' ' + client.last_name,
        'full_name_receiver': user.first_name + ' ' + user.last_name,
        'date': appointment.start_date,
        'time': appointment.start_time
    }
    html_content = html_template.render(context)

    msg = EmailMultiAlternatives(subject=subject, from_email=from_email, to=[to])
    msg.attach_alternative(html_content, "text/html")

    try:
        msg.send()
    except Exception as ex:
        print(ex)

@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def booking_api(request):
    if request.method == 'POST':
        data = request.data

        start_time = request.data.get('start_time', None)
        end_time = request.data.get('end_time', None)
        room = request.data.get('room', None)
        if start_time and room and end_time:
            start_date = request.data.get('start_date', None)
            if start_date:
                start_date = datetime.fromtimestamp(start_date)

            else:
                start_date = datetime.now().date()
            start_time = datetime.strptime(start_time, "%H:%M:%S").time()
            end_time = datetime.strptime(end_time, "%H:%M:%S").time()
            start_datetime = datetime.combine(start_date.date(), start_time)
            end_datetime = datetime.combine(start_date.date(), end_time)
            consecutive_booking = Booking.objects.filter(Q(start_date=start_date) &  (Q(end_time=start_time) | Q(start_time=end_time))).exists()
            if not consecutive_booking:
                one_hour_before_start_time = start_datetime - timedelta(hours=1)
                one_hour_after_start_time = end_datetime + timedelta(hours=1)

                conflicting_bookings = Booking.objects.filter(room=room, start_date=start_date, end_time__gt=one_hour_before_start_time.time(), start_time__lte=one_hour_before_start_time.time())

                if conflicting_bookings.exists():
                    return Response({'msg': 'This is not available slot'}, status=status.HTTP_400_BAD_REQUEST)

                conflicting_bookings = Booking.objects.filter(room=room, start_date=start_date, start_time__lt=one_hour_after_start_time.time(), end_time__gte=one_hour_after_start_time.time())
                if conflicting_bookings.exists():
                    return Response({'msg': 'This is not available slot'}, status=status.HTTP_400_BAD_REQUEST)




        if 'selected_dates' in data:
            selected_dates = data.get('selected_dates')
            if selected_dates:
                dates = [datetime.fromtimestamp(date) for date in selected_dates]
                data['selected_dates'] = dates
        data['start_date'] = datetime.fromtimestamp(data.get('start_date'))

        validated_data = validate_booking(data)

        booking, room = create_booking(validated_data)

        with schema_context('public'):
            AdminBookingDetails.objects.create(id=booking.id, start_date=booking.start_date,
                                               start_time=booking.start_time, end_time=booking.end_time,
                                               location=room.location, room=room,
                                               room_cost=booking.room_cost,
                                               status=booking.status, provider=request.user.provider)
        response_data = model_to_dict(booking)

        return Response(response_data, status=status.HTTP_201_CREATED)

    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-created_at')
        bookings = Booking.objects.all().order_by(ordering)
        bookings = bookings.annotate(duration=F('end_time') - F('start_time'))

        bookings_data = list()
        for booking in bookings:
            room = booking.room
            location = {
                'id': booking.location.id,
                'name': booking.location.name
            }
            provider = booking.created_by
            room_data = {'id': room.id, 'location': room.location.id, 'name': room.name}
            provider_data = {'id': provider.id, 'first_name': provider.first_name, 'middle_name': provider.middle_name,
                             'last_name': provider.last_name, 'avatar': provider.avatar}

            booking_data = model_to_dict(booking)
            booking_data['room'] = room_data
            booking_data['location'] = location
            booking_data['provider'] = provider_data

            bookings_data.append(booking_data)

        return paginated_queryset_response(queryset=bookings_data, request=request)


@api_view(('GET', 'PATCH', 'DELETE'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def booking_id_api(request, booking_id):
    instance = get_object_or_404(Booking, id=booking_id)
    if request.method == 'PATCH':
        data = request.data
        if instance.status == BookingStatusChoices.CANCELED.value:
            raise ValidationError({'msg': 'This booking had been cancelled.'})
        if instance.status == BookingStatusChoices.COMPLETE.value:
            raise ValidationError({'msg': 'This booking had been completed.'})

        status = request.data.get('status', None)
        if str(status) == str(BookingStatusChoices.CANCELED.value):
            recurring_booking = False
            if 'recurring' in request.data and \
                    request.data['recurring'] is True:
                recurring_booking = True
            cancel_booking(request.user, instance, recurring_booking)

            return Response({'msg': 'Booking has been canceled'}, status=200)
        else:
            start_time = request.data.get('start_time', None)
            room = request.data.get('room', None)
            if start_time and room:
                start_date = request.data.get('start_date', None)
                if start_date:
                    start_date = datetime.fromtimestamp(start_date)

                else:
                    start_date = datetime.now()
                start_time = datetime.strptime(start_time, "%H:%M:%S").time()
                start_datetime = datetime.combine(start_date.date(), start_time)
                consecutive_booking = Booking.objects.filter(start_date=start_date, end_time=start_time).exists()
                if not consecutive_booking:
                    desired_start_time = start_datetime - timedelta(hours=1)
                    existing_bookings = Booking.objects.filter(
                        Q(room=room, start_date=start_date, end_time__gt=desired_start_time.time())
                        # Check for bookings ending within 1 hour
                    )
                    if existing_bookings:
                        return Response({'msg': 'This is not available slot'}, status=status.HTTP_400_BAD_REQUEST)

            data = validate_update_booking_api(instance, data)
            for field_name, field_value in data.items():
                if hasattr(Booking, field_name):
                    if field_name == 'selected_dates':
                        updated_data = [datetime.fromtimestamp(date) for date in field_value]
                        setattr(instance, field_name, updated_data)
                    else:
                        setattr(instance, field_name, field_value)

            instance.save()
            user = request.user
            if user.is_provider:
                instance = handle_appointment_on_booking_update(user.provider, instance, data)

            with schema_context('public'):
                admin_booking = get_object_or_404(AdminBookingDetails, id=instance.id)
                for field_name, field_value in data.items():
                    if hasattr(AdminBookingDetails, field_name):
                        if field_name == 'location':
                            field_value = Location.objects.filter(id=field_value).first()
                        if field_name == 'room':
                            field_value = Room.objects.filter(id=field_value).first()
                        setattr(admin_booking, field_name, field_value)
            admin_booking.save()

            instance = model_to_dict(instance,
                                     exclude=['created_at', 'updated_at'])

            return Response(instance)

    if request.method == 'DELETE':
        _id = instance.id
        instance.delete()
        with schema_context('public'):
            query = get_object_or_404(AdminBookingDetails, id=_id)
            query.delete()
        return Response({"msg": "Booking has been deleted"}, status=204)

    if request.method == 'GET':

        room = Room.objects.filter(id=instance.room).first()
        booking_data = {
            'id': instance.id,
            'booking_number': instance.booking_number if instance else None,

            'location': {
                'id': room.location.id if room else None,
                'name': room.location.name if room else None
            },
            'room': {
                'id': room.id if room else None,
                'name': room.name if room else None
            },
            'start_date': instance.start_date.timestamp(),
            'end_time': instance.end_time,
            'start_time': instance.start_time,
            'status': instance.status,
            'select_dates': instance.select_dates,
            'selected_dates': instance.selected_dates,
            'created_at': instance.created_at.timestamp(),
            'room_cost': instance.room_cost
        }
        return Response(booking_data, status=200)


@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def appointment_api(request, booking_id=None):
    if request.method == 'POST':
        data = request.data
        if booking_id:
            data['booking'] = get_object_or_404(Booking, id=booking_id)

        if 'selected_dates' in data:
            selected_dates = data.get('selected_dates')
            if selected_dates:
                dates = [datetime.fromtimestamp(date) for date in selected_dates]
                data['selected_dates'] = dates

        data['start_date'] = datetime.fromtimestamp(data.get('start_date'))

        data = validate_appointment(request, data)

        app = create_appointment(request, data)
        if app.booking:
            if app.booking.select_dates is True and app.booking.selected_dates is not None:
                repeat_bookings(request.user, app)
        if (app.weekly_repeat or app.select_dates) is True and (
                (int(app.number_of_weeks) or len(app.selected_dates)) > 0):
            if app.booking is None:
                repeat_appointments(request.user, app)

        if app.booking:
            with schema_context('public'):
                instance = AdminBookingDetails.objects.filter(id=app.booking.id).first()
                instance.appointments_start_time = app.start_time
                instance.appointments_end_time = app.end_time

                instance.save()

        return Response({"msg": "Appointment has been created"}, status=status.HTTP_201_CREATED)

    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-booking__start_date')
        queryset = Appointment.objects.all().order_by(ordering, '-start_time')
        if booking_id:
            queryset = queryset.filter(booking__id=booking_id)
        appointments = []
        for appointment in queryset:
            booking = {}
            if appointment.booking:
                booking = appointment.booking
                room = booking.room
                location_data = {
                    'id': booking.location.id,
                    'name': booking.location.name
                }
                room_data = {'id': room.id, 'location': room.location.id, 'name': room.name}
                booking = model_to_dict(booking)
                booking['room'] = room_data
                booking['location'] = location_data

            provider_location_data = None
            if appointment.provider_location:
                provider_location_data = {
                    'id': appointment.provider_location.id,
                    'name': appointment.provider_location.name
                }
            clients = appointment.clients.all()
            client_data = list()
            for client in clients:
                client_data.append({'id': client.id, 'first_name': client.first_name, 'middle_name': client.middle_name,
                                    'last_name': client.last_name})

            appointment = model_to_dict(appointment)
            appointment['booking'] = booking
            appointment['provider_location'] = provider_location_data if provider_location_data else ""
            appointment['clients'] = client_data
            appointments.append(appointment)

        return paginated_queryset_response(appointments, request=request)


@api_view(('GET', 'PATCH', 'DELETE'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser | IsClient])
@transaction.atomic
def appointment_id_api(request, appointment_id=None):
    appointment = get_object_or_404(Appointment, id=appointment_id)
    if request.method == 'GET':
        booking = appointment.booking
        if booking:
            room = booking.room
            room = Room.objects.filter(id=room).first()
            location_data = {
                'id': room.location.id,
                'name': room.location.name
            }
            room_data = {'id': room.id, 'location': room.location.id, 'name': room.name}
            cancel_date = booking.cancel_date
            booking = model_to_dict(booking)
            booking['room'] = room_data
            booking['cancel_date'] = cancel_date.timestamp() if cancel_date else None
            booking['location'] = location_data
            booking['start_date'] = booking.get('start_date').timestamp()
            select_dates = booking.get('selected_dates', None)
            if select_dates:
                booking['selected_dates'] = [date.timestamp() for date in select_dates]

        provider_location_data = None
        if appointment.provider_location:
            provider_location_data = {
                'id': appointment.provider_location.id,
                'name': appointment.provider_location.name
            }
        # clients = appointment.clients.all()
        # client_data = list()
        # # for client in clients:
        # #     client_data.append({'id': client.id, 'first_name': client.first_name, 'middle_name': client.middle_name,
        # #                         'last_name': client.last_name})
        response_appointment = model_to_dict(appointment, exclude=['access_granted', 'access_granted_at',
                                                                   'checked_in_at', 'is_recurrent_of', 'is_sent',
                                                                   'clients'])
        response_appointment['booking'] = booking
        # response_appointment['start_date'] = appointment.start_date.timestamp()
        response_appointment['provider_location'] = provider_location_data if provider_location_data else ""
        response_appointment['appointment_services'] = appointment_services(appointment)

        start_date = appointment.start_date.timestamp() if appointment.start_date else None

        if start_date is None and appointment.booking:
            response_appointment['start_date'] = appointment.booking.start_date.timestamp()
        else:
            response_appointment['start_date'] = start_date

        return Response(response_appointment, 200)

    if request.method == 'DELETE':
        if appointment.booking:
            booking = appointment.booking
            with schema_context('public'):
                instance = AdminBookingDetails.objects.filter(id=booking.id).first()
                instance.delete()
            booking.delete()

        appointment.delete()
        return Response({'msg': 'Appointment has been deleted'}, status=204)

    if request.method == 'PATCH':
        user = request.user
        request_data = request.data.copy()
        if user.is_client:
            if 'checked_in' in request_data:
                request_data['checked_in_at'] = datetime_helper.now()
                # appointment.checked_in = True
                # appointment.checked_in_at = datetime_helper.now()
                # appointment.save()
            else:
                return Response(400)
        booking_pk = request.GET.get('booking_pk', None)
        if booking_pk:
            request_data['booking'] = booking_pk

        if 'status' in request.data:
            if appointment.status == str(AppointmentStatusChoices.CANCELLED.value):
                raise ValidationError('This appointment had been cancelled.')

            booking = appointment.booking if appointment.booking else None
            status = request.data.get('status', appointment.status)
            if status == str(AppointmentStatusChoices.CANCELLED.value):
                if booking:
                    cancel_appointment(request, appointment, booking)

        validate_appointment_update(appointment, request, request.data)

        update_appointment(request, instance=appointment, validated_data=request.data)

        if 'checked_in' in request.data and user.is_client:
            provider = Provider.objects.filter(id=user.client.user_provider).first()
            send_checked_in_template(provider, user.client, appointment)

        return Response({"msg": "Appointment updated"}, status=200)


@api_view(('POST',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def appointment_client_add_api(request, appointment_pk=None):
    if request.method == 'POST':
        if appointment_pk is None:
            raise ValidationError('Appointment id must be provided')

        instance = Appointment.objects.get(id=appointment_pk)

        services = request.data.get('appointment_services', None)
        clients = []
        for service in services:
            client = service['id']
            clients.append(client)
            if client not in instance.clients.all():
                instance.clients.add(client)
                instance.save()

        if services:
            save_client_services(instance, services)

        if instance.appointment_invoice.exists():
            for client in clients:
                invoice = ClientInvoice.objects.create(appointment=instance, client__id=client,
                                                       provider=instance.provider, issue_date=datetime.now(),
                                                       due_date=(datetime.now() + timedelta(days=7)))
                services = AppointmentClientServices.objects.filter(appointment=instance, client__id=client)
                total_fees = Decimal(0.0)
                for service in services:
                    service['invoice'] = invoice.id
                    service['client'] = client
                    total_fees += service.fees
                    if 'date' in service and service['date'] is not None:
                        service['date'] = datetime.fromtimestamp(service.get('time'))
                    InvoiceServices.objects.create(**service)
                invoice.fees = total_fees
                invoice.save()

        return Response({"msg": "Client Added"}, status=status.HTTP_200_OK)


@api_view(('POST',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def appointment_client_delete_api(request, appointment_pk=None):
    if appointment_pk is None:
        raise ValidationError('Appointment id must be provided')

    instance = Appointment.objects.get(id=appointment_pk)

    client = request.data.get('client', None)

    instance.clients.remove(client)
    instance.save()

    AppointmentClientServices.objects.filter(client__id=client).delete()
    ClientInvoice.objects.filter(client=client, appointment=instance).delete()

    return Response({"msg": "Client Deleted"}, status=status.HTTP_200_OK)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsClient])
@transaction.atomic
def previous_next_appointment_api(request):
    user = request.user
    provider = None
    if user.is_client:
        provider = Provider.objects.filter(id=user.client.user_provider).first()
    elif user.is_provider:
        provider = user.provider

    current_datetime = datetime_helper.now()

    previous_appointment = Appointment.objects.filter(
        Q(clients=user,
        start_date__date__lte=current_datetime.date()) |
        Q(clients=user,
          booking__start_date__date__lte=current_datetime.date())
    ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by(
        '-start_date').all()

    previous_appointment = previous_appointment.exclude(
        start_date__date=current_datetime.date(),
        start_time__gt=current_datetime.time()
    )[:1]

    next_appointment = Appointment.objects.filter(
        clients=user,
        start_date__date__gte=current_datetime.date(),
    ).exclude(status=AppointmentStatusChoices.CANCELLED.value).all()
    next_appointment = next_appointment.exclude(start_date__date=current_datetime.date(),
                                                end_time__lt=current_datetime.time()).order_by(
        'start_date')[:1]
    if previous_appointment:
        previous_appointment = previous_appointment.first()
        start_date = previous_appointment.start_date.timestamp() if previous_appointment.start_date else None

        previous_appointment = model_to_dict(previous_appointment, exclude=['clients'])
        if start_date:
            previous_appointment['start_date'] = start_date

        previous_appointment['provider'] = {'id': provider.id,
                                        'first_name': provider.first_name,
                                        'middle_name': provider.middle_name,
                                        'last_name': provider.last_name
                                        }

    if next_appointment:
        next_appointment = next_appointment.first()
        start_date = next_appointment.start_date.timestamp() if next_appointment.start_date else None
        next_appointment = model_to_dict(next_appointment, exclude=['clients'])
        if start_date:
            next_appointment['start_date'] = start_date

        next_appointment['provider'] = {'id': provider.id,
        'first_name': provider.first_name,
        'middle_name': provider.middle_name,
        'last_name': provider.last_name
        }

    if next_appointment:
        booking_id = next_appointment.get('booking', None)
        if booking_id:
            with schema_context("public"):
                admin_booking = AdminBookingDetails.objects.filter(id=booking_id).first()
                if admin_booking:
                    next_appointment['booking'] = {
                        'id': booking_id,
                        'location': {
                            'id': admin_booking.location.id,
                            'name': admin_booking.location.name,
                            'address': admin_booking.location.address
                        },
                        'room': {
                            'id': admin_booking.room.id,
                            'name': admin_booking.room.name
                        }
                    }
        else:
            provider_location = next_appointment.get('provider_location', None)
            if provider_location:
                provider_location = ProviderLocation.objects.filter(id=provider_location).first()
                if provider_location:
                    next_appointment['provider_location'] = {
                        'id': provider_location.id,
                        'name': provider_location.name,
                        'address': provider_location.address,
                        'city': provider_location.city,
                        'zipcode': provider_location.zipcode,
                        'state': provider_location.state
                    }

    if previous_appointment:
        booking_id = previous_appointment.get('booking', None)
        if booking_id:
            with schema_context("public"):
                admin_booking = AdminBookingDetails.objects.filter(id=booking_id).first()
                if admin_booking:
                    previous_appointment['booking'] = {
                        'id': booking_id,
                        'location': {
                            'id': admin_booking.location.id,
                            'name': admin_booking.location.name,
                            'address': admin_booking.location.address
                        },
                        'room': {
                            'id': admin_booking.room.id,
                            'name': admin_booking.room.name
                        }
                    }

        else:
            provider_location = previous_appointment.get('provider_location', None)
            if provider_location:
                provider_location = ProviderLocation.objects.filter(id=provider_location).first()
                if provider_location:
                    previous_appointment['provider_location'] = {
                        'id': provider_location.id,
                        'name': provider_location.name,
                        'address': provider_location.address,
                        'city': provider_location.city,
                        'zipcode': provider_location.zipcode,
                        'state': provider_location.state
                    }

    return Response({'next_appointment': next_appointment,
                     'previous_appointment': previous_appointment})



@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsAdminUser])
@transaction.atomic
def attendance_api(request):
    if request.method == 'POST':
        data = request.data
        id = data.get('appointment')
        client_id = data.get('client')
        data['appointment'] = get_object_or_404(Appointment, id=id)
        data['client'] = get_object_or_404(Client, id=client_id)
        Attendance.objects.create(**data)

        return Response({"msg": "Attendance has been created"}, status=status.HTTP_201_CREATED)

    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-created_at')
        queryset = Attendance.objects.all().order_by(ordering).values('created_at', 'id', 'client', 'status')

        return paginated_queryset_response(queryset)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider | IsClient])
@transaction.atomic
def calendar_api(request):
    if request.method == 'GET':
        user = request.user
        if user.is_client:
            querysets = Appointment.objects.filter(clients=user)
        else:
            querysets = Appointment.objects.all()

        responses_data = []
        for queryset in querysets:
            if queryset:
                start_timestamp = int(queryset.start_date.timestamp())
                response_data = {
                    'start_date': str(start_timestamp),
                    'start_time': queryset.start_time.strftime('%H:%M:%S'),
                    'end_time': queryset.end_time.strftime('%H:%M:%S'),
                    # 'booking': {
                    #     'id': str(queryset.booking.id),
                    #     # 'room': {
                    #     #     'id': str(appointment.booking.room),
                    #     #     'name': 'Test Location',
                    #     # }
                    # },
                    'telehealth': queryset.telehealth,
                    'telehealth_zoom_id': queryset.telehealth_zoom_id,
                    'telehealth_google_id': queryset.telehealth_google_id
                }
                if queryset.booking:
                    with schema_context("public"):
                        admin_booking = AdminBookingDetails.objects.filter(id=queryset.booking.id).first()
                        if admin_booking:
                            response_data['booking'] = {
                                'id': admin_booking.id,
                            }
                            response_data['booking']['location'] = {
                                'id': admin_booking.location.id,
                                'name': admin_booking.location.name
                            }
                            response_data['booking']['room'] = {
                                'id': admin_booking.room.id,
                                'name': admin_booking.room.name
                            }
                else:
                    response_data['booking'] = None
                    provider_location = queryset.provider_location
                    response_data['provider_location'] = {
                        'id': provider_location.id,
                        'name': provider_location.name,
                        'address': provider_location.address,
                        'city': provider_location.city,
                        'zipcode': provider_location.zipcode,
                        'state': provider_location.state

                    }
                responses_data.append(response_data)
        return paginated_queryset_response(responses_data, request)

        # response_data = []
        # for appointment in querysets:
        #     start_timestamp = int(appointment.start_date.timestamp())
        #     appointment_data = {
        #         'start_date': str(start_timestamp),
        #         'start_time': appointment.start_time.strftime('%H:%M:%S'),
        #         'end_time': appointment.end_time.strftime('%H:%M:%S'),
        #         'provider_location': None,  # You may need to populate this based on your logic
        #         'booking': {
        #             'id': str(appointment.booking.id),
        #             'location': {
        #                 'id': str(appointment.booking.location.id),
        #                 'name': appointment.booking.location.name,
        #                 'address': appointment.booking.location.address,
        #             },
        #             'room': {
        #                 'id': str(appointment.booking.room),
        #                 'name': 'Test Location',  # You may need to fetch the room name from your model
        #             }
        #         },
        #         'telehealth': appointment.telehealth,
        #         'telehealth_zoom_id': appointment.telehealth_zoom_id,
        #         'telehealth_google_id': appointment.telehealth_google_id
        #     }
        #     response_data.append(appointment_data)
        #
        # return paginated_queryset_response(response_data, request)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def appointment_notes_api(request):
    if request.method == 'GET':
        queryset = Appointment.objects.filter(start_date__date__lt=datetime.now().date())
        queryset = list(appointment_filter_queryset(queryset=queryset, request=request))
        queryset = list(date_range_filter_queryset(queryset=queryset, request=request))
        client_id = request.query_params['clients']
        notes = None
        if client_id:
            notes = AppointmentNotes.objects.filter(client__id=client_id, appointment__isnull=True)
            notes = list(filtering_admin_notes(request, notes))

        for query in queryset:
            appointment_notes = list()
            notes = query.appointment_notes.all()
            for note in notes:
                files = list()
                notes_files = note.appointment_notes_files
                note = model_to_dict(note,
                                     fields=['id', 'appointment', 'title', 'content', 'type', 'date', 'client', 'time',
                                             'note'])
                for file in notes_files:
                    files.append(model_to_dict(file, fields=['id', 'title', 'file']))
                note = model_to_dict(note)
                note['appointment_notes_files'] = files
                appointment_notes.append(note)
            query = model_to_dict(query, fields=['id', 'start_time', 'end_time', 'start_date', 'status'])
            query['appointment_notes'] = appointment_notes

        if notes:
            admin_notes = model_to_dict(note for note in notes)
            merged_data = sorted(
                queryset + admin_notes,
                key=lambda x: x.get('start_date') or x.get('date', ''),
                reverse=True
            )
            response_data = {'data': merged_data}
        else:
            response_data = {'data': queryset}
        return Response({'result': response_data['data']})


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def provider_calendar_list(request):
    user = request.user
    if user.is_provider:
        appointment = Appointment.objects.filter(booking__isnull=True,
                                                 provider_location__isnull=False)
        appointment = date_range_filter_queryset(request, appointment)

        queryset = Booking.objects.all()
        queryset = queryset.prefetch_related(
            'appointments'
        )
        queryset = queryset.annotate(duration=F('end_time') - F('start_time'))

        queryset = booking_filter_queryset(request, queryset)
        queryset = booking_date_range_filter(request, queryset)

        # queryset = (get_queryset()).select_related('location', 'room').prefetch_related(
        #     'appointments__clients')
        output_json = []
        offset = int(request.GET.get('offset', 0))
        limit = int(request.GET.get('limit', 10))
        querysets = queryset[offset:offset + limit]
        for booking in querysets:
            room = Room.objects.filter(id=booking.room).first()
            output_client = []
            appointments = None
            try:
                appointments = booking.appointments
            except Booking.appointments.RelatedObjectDoesNotExist:
                appointments = None

            if appointments:
                output_client = [
                    {'id': client.id, 'first_name': client.first_name, 'middle_name': client.middle_name,
                     'last_name': client.last_name} for client in appointments.clients.all()]
            output = {
                'id': booking.id,
                'created_at': booking.created_at,
                'status': booking.status,
                'start_date': datetime.timestamp(booking.start_date),
                'start_time': booking.start_time,
                'end_time': booking.end_time,
                'appointment_id': booking.appointments.id if appointments else '',
                'appointment_status': booking.appointments.status if appointments else '',
                'appointment_start_time': appointments.start_time if appointments else '',
                'appointment_end_time': appointments.end_time if appointments else '',
                'room_cost': booking.room_cost,
                'clients': output_client
            }
            if appointments and appointments.start_date:
                output['appointment_start_date'] = appointments.start_date.timestamp()
            else:
                output['appointment_start_date'] = booking.start_date.timestamp()
            if room:
                output['location_id'] = room.location.id if room.location else None
                output['location_name'] = room.location.name if room.location else None
                output['room_id'] = room.id if room else None
                output['room_name'] = room.name if room else None
            output_json.append(output)
        if appointment:
            for query in appointment:
                output_client = [
                    {'id': client.id, 'first_name': client.first_name, 'middle_name': client.middle_name,
                     'last_name': client.last_name} for client in query.clients.all()]

                output_json.append({
                    'appointment_id': query.id,
                    'location_id': query.provider_location.id if query.provider_location else None,
                    'location_name': query.provider_location.name if query.provider_location else None,
                    'created_at': query.created_at,
                    'appointment_status': query.status,
                    'appointment_start_date': datetime.timestamp(query.start_date),
                    'appointment_start_time': query.start_time,
                    'appointment_end_time': query.end_time,
                    'clients': output_client
                })

        return Response({'count': len(output_json), 'offset': offset, 'limit': limit, 'result': output_json})


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def calendar_list(request, appointment_id=None):
    if appointment_id:
        appointment = Appointment.objects.filter(id=appointment_id).first()
    else:
        raise ValidationError({'msg': 'Appointment id can not be null'})

    if appointment:
        appointment_data = {
            'id': appointment.id,
            'status': appointment.status,
            'start_time': appointment.start_time.strftime('%H:%M:%S'),
            'end_time': appointment.end_time.strftime('%H:%M:%S'),
            'start_date': appointment.start_date.timestamp() if appointment.start_date else None,
            'appointment_services': appointment_services(appointment),
            'weekly_repeat': appointment.weekly_repeat,
            'number_of_weeks': appointment.number_of_weeks,
            'weekly_repeat_gap': appointment.weekly_repeat_gap,
            'select_dates': appointment.select_dates,
            'telehealth': appointment.telehealth,
            'google': appointment.google,
            'zoom': appointment.zoom,
            'selected_dates': appointment.selected_dates,
            'telehealth_google_id': appointment.telehealth_google_id,
            'telehealth_zoom_id': appointment.telehealth_zoom_id,
            'description': appointment.description
        }

        if appointment_data['start_date'] is None and appointment.booking:
            appointment_data['start_date'] = appointment.booking.start_date.timestamp()

        if appointment.booking:
            booking = appointment.booking
            room = Room.objects.filter(id=booking.room).first()
            location = room.location if room else None
            appointment_data['booking'] = {
                'id': booking.id,
                'booking_number': booking.booking_number if booking else None,

                'location': {
                    'id': location.id if location else '',
                    'name': location.name if location else '',
                    'room_map': location.room_map.url if location and location.room_map else '',
                    'room_map_mapping': location.room_map_mapping if location else ''
                },
                'room': {
                    'id': room.id if room else '',
                    'name': room.name if room else ''
                },
                'start_date': booking.start_date.timestamp(),
                'end_time': booking.end_time,
                'start_time': booking.start_time,
                'status': booking.status,
                'select_dates': booking.select_dates,
                'selected_dates': booking.selected_dates,
                'created_at': booking.created_at.timestamp(),
                'room_cost': booking.room_cost
            }
        else:
            provider_location = appointment.provider_location
            if provider_location:
                appointment_data['provider_location'] = {
                    'id': provider_location.id,
                    'name': provider_location.name
                }

        return Response(appointment_data)
    else:
        return Response({'msg': 'Appointment Not Found'}, status=status.HTTP_204_NO_CONTENT)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def provider_booking_list(request):
    try:
        ordering = request.GET.get('ordering', '-start_date')
        queryset = Booking.objects.all().order_by(ordering)
        queryset = booking_filter_queryset(request, queryset)
        queryset = booking_date_range_filter(request, queryset)

        output_json = []
        offset = int(request.GET.get('offset', 0))
        limit = int(request.GET.get('limit', 10))
        querysets = queryset[offset:offset + limit]
        for booking in querysets:
            output_client = []
            appointments = Appointment.objects.filter(booking=booking).first()
            if appointments:
                output_client = [
                    {'id': client.id, 'first_name': client.first_name, 'middle_name': client.middle_name,
                     'last_name': client.last_name} for client in booking.appointments.clients.all()]
            room = Room.objects.filter(id=booking.room).first()
            output = {
                'id': booking.id,
                'created_at': booking.created_at,
                'status': booking.status,
                'start_date': datetime.timestamp(booking.start_date) if booking.start_date else None,
                'start_time': booking.start_time,
                'end_time': booking.end_time,
                'appointment_id': booking.appointments.id if appointments else '',
                'appointment_status': booking.appointments.status if appointments else '',
                'appointment_start_time': appointments.start_time if appointments else '',
                'appointment_end_time': appointments.end_time if appointments else '',
                'room_cost': booking.room_cost,
                'clients': output_client
            }
            if appointments:
                output['appointment_start_date'] = appointments.start_date.timestamp() if appointments.start_date else None
            else:
                output['appointment_start_date'] = None
            if room:
                output['location_id'] = room.location.id if room.location else None
                output['location_name'] = room.location.name if room.location else None
                output['room_id'] = room.id if room else None
                output['room_name'] = room.name if room else None

            output_json.append(output)
        return Response({'count': queryset.count(), 'offset': offset, 'limit': limit, 'result': output_json})
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to get provider_booking_list because of {str(e)}'
        }, status=400)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def appointment_tab_list(request):
    queryset = Appointment.objects.all()
    queryset = queryset.order_by('-start_date', '-start_time')
    queryset = appointment_filter_queryset(request, queryset)
    queryset = date_range_filter_queryset(request, queryset)
    appointments = []
    for appointment in queryset:
        location = appointment.provider_location

        client_data = []
        for client in appointment.clients.all():
            client_data.append(
                {'id': client.id,
                 'first_name': client.first_name,
                 'last_name': client.last_name
                 }
            )
        app = {
            'id': appointment.id,
            'clients': client_data,
            'status': appointment.status,
            'start_time': appointment.start_time.strftime('%H:%M:%S'),
            'end_time': appointment.end_time.strftime('%H:%M:%S'),
            'start_date': appointment.start_date.timestamp()
        }
        if location:
            app['provider_location']= {
                'id': location.id,
                'name': location.name
            }
        if appointment.booking:
            booking = appointment.booking
            if booking:
                room = booking.room

                app['booking'] = {
                    'id': booking.id,
                    'start_time': booking.start_time.strftime('%H:%M:%S'),
                    'end_time': booking.end_time.strftime('%H:%M:%S'),
                    'start_date': booking.start_date.timestamp()
                }
                room = Room.objects.filter(id=room).first()
                if room:
                    app['booking']['room'] = {
                        'id': room.id,
                        'name': room.name
                    }
                    app['booking']['location'] = {
                        'id': room.location.id if room.location else '',
                        'name': room.location.name if room.location else ''
                    }
        appointments.append(app)

    return paginated_queryset_response(appointments, request)


@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def appointment_tab_id_api(request, appointment_id):
    appointment = Appointment.objects.filter(id=appointment_id).first()
    location = appointment.provider_location

    appointment_data = {
        'id': appointment.id,
        'status': appointment.status,
        'start_time': appointment.start_time.strftime('%H:%M:%S'),
        'end_time': appointment.end_time.strftime('%H:%M:%S'),
        'start_date': appointment.start_date.timestamp(),
        'provider_location': {
            'id': location.id,
            'name': location.name
        },
        'appointment_services': appointment_services(appointment)
    }

    return Response(appointment_data, status=status.HTTP_200_OK)

@api_view(('GET',))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def get_appointments_api(request):
    ordering = request.GET.get('ordering', '-start_date')
    count = Appointment.objects.all().count()
    appointments = Appointment.objects.all()
    params = request.query_params
    if 'start_date' in params:
        start_date = params['start_date']
        appointments = appointments.filter(Q(start_date__date__gte=start_date) | Q(booking__start_date__date__gte=start_date))
    appointments = appointments.order_by(ordering)
    response_data = []
    for appointment in appointments:
        appointment_data = {
            'id': appointment.id,
            'status': appointment.status,
            'start_time': appointment.start_time.strftime('%H:%M:%S'),
            'end_time': appointment.end_time.strftime('%H:%M:%S'),
            'start_date': appointment.start_date.timestamp() if appointment.start_date else None,
            'appointment_services': appointment_services(appointment),
            'weekly_repeat': appointment.weekly_repeat,
            'number_of_weeks': appointment.number_of_weeks,
            'weekly_repeat_gap': appointment.weekly_repeat_gap,
            'select_dates': appointment.select_dates,
            'telehealth': appointment.telehealth,
            'google': appointment.google,
            'zoom': appointment.zoom,
            'selected_dates': appointment.selected_dates,
            'telehealth_google_id': appointment.telehealth_google_id,
            'telehealth_zoom_id': appointment.telehealth_zoom_id,
            'description': appointment.description,
            'booking': None,
            'provider_location': None
        }

        client_data = []
        for client in appointment.clients.all():
            client_data.append({
                'id': client.id,
                'first_name': client.first_name,
                'middle_name': client.middle_name,
                'last_name': client.last_name
            })

        appointment_data['clients'] = client_data

        if appointment_data['start_date'] is None and appointment.booking:
            appointment_data['start_date'] = appointment.booking.start_date.timestamp()

        if appointment.booking:
            booking = appointment.booking
            room = Room.objects.filter(id=booking.room).first()
            appointment_data['booking'] = {
                'id': booking.id,
                'booking_number': booking.booking_number if booking else None,

                'location': {
                    'id': room.location.id if room else None,
                    'name': room.location.name if room else None
                },
                'room': {
                    'id': room.id if room else None,
                    'name': room.name if room else None
                },
                'start_date': booking.start_date.timestamp(),
                'end_time': booking.end_time,
                'start_time': booking.start_time,
                'status': booking.status,
                'select_dates': booking.select_dates,
                'selected_dates': booking.selected_dates,
                'created_at': booking.created_at.timestamp(),
                'room_cost': booking.room_cost
            }
        else:
            provider_location = appointment.provider_location
            if provider_location:
                appointment_data['provider_location'] = {
                    'id': provider_location.id,
                    'name': provider_location.name
                }
        response_data.append(appointment_data)
    return Response({'result': response_data, 'count':count})
